/* this file contain some code that may be obsoluted */

/* virtual memory routine */

int
expand_pde(void *addr, uint32_t *create_perm)
{
     uint32_t idx;
     idx = PAGEDIR_IDX(addr);

     uint32_t l;
     int err;
     physaddr_t naddr;
     uint32_t i;

	 uint32_t pde;

	 pde = spinlock_pde(idx);

	 /* expand if really need */
     if (pde & PTE_P)
     {
		  if (!(pde & PTE_COW))
		  {
			   err = 0;
			   goto expand_pde_exit;
		  }

		  l = pcs_push_level();

		  if ((err = kpage_alloc_push(&naddr)) != 0)
		  {
			   pcs_clear_level(l);
			   goto expand_pde_exit;
		  }

		  vpt[PAGE_NUM(TVPT)] = naddr | PTE_W | PTE_P;
		  vpt[PAGE_NUM(TSPAGE)] = PTE_ADDR(pde) | PTE_P;
		  tlbflush();
     
		  ptab_entry_t *svpt;
		  svpt = tspage;
		  // svpt = (ptab_entry_t *)vpt + (idx << (PDX_SHIFT - PTX_SHIFT));
	  
		  for (i = 0; i != PAGETAB_COUNT; ++i)
		  {
			   if (svpt[i] & PTE_P)
			   {
					if ((svpt[i] & (PTE_W | PTE_COW)) && !(svpt[i] & PTE_SHARED))
					{
						 tvpt[i] = (svpt[i] | PTE_COW) & ~(uint32_t)PTE_W;
						 
						 /* 不能改变源 */
						 // svpt[i] = tvpt[i];
					}
					else tvpt[i] = svpt[i];

					kpage_inc_ref(PTE_ADDR(tvpt[i]));
			   }
		  }

		  kpage_dec_ref(PTE_ADDR(pde));
		  pde = (vpt[PAGE_NUM(TVPT)] | (pde & PTE_USER) | PTE_W) & ~(uint32_t)PTE_COW;

		  pcs_pop_level(l);
		  tlbflush();

		  err = 0;
     }
     else if (create_perm != NULL)
     {
		  l = pcs_push_level();

		  if ((err = kpage_alloc_push(&naddr)) != 0)
		  {
			   pcs_clear_level(l);
			   goto expand_pde_exit;
		  }

		  vpt[PAGE_NUM(TVPT)] = naddr | PTE_W | PTE_P;
		  tlbflush();

		  memset(tvpt, 0, PAGE_SIZE);
	  
		  pde = PTE_ADDR(vpt[PAGE_NUM(TVPT)]) | *create_perm | PTE_P;
		  // cprintf(" [expand_pde] create vpd[%x] = %x, %b\n", idx, PTE_ADDR(pde), *create_perm);
	  
		  *create_perm = 0;
	  
		  pcs_pop_level(l);
		  tlbflush();

		  err = 0;
     }
     else err = -E_FAULT;

expand_pde_exit:

	 release_pde(idx, pde);
	 return err;
}

int
expand_pte(void *addr, uint32_t *create_perm)
{
     uint32_t idx;

     addr = PAGE_ALIGN(addr);
     
     uint32_t l, i;
     int err;
     
     uint32_t cp;

     if (create_perm == NULL)
     {
		  if ((err = expand_pde(addr, NULL)) != 0)
		  {
			   return err;
		  }
     }
     else
     {
		  cp = *create_perm;
		  if ((err = expand_pde(addr, &cp)) != 0)
		  {
			   return err;
		  }
     }

     idx = PAGE_NUM(addr);

	 uint32_t pte;
     physaddr_t naddr;

	 pte = spinlock_pte(idx);
	 
	 /* expand if really need */
     if (pte & PTE_P)
     {

		  if (!(pte & PTE_COW))
		  {
			   err = 0;
			   goto expand_pte_exit;
		  }

		  l = pcs_push_level();

		  if ((err = kpage_alloc_push(&naddr)) != 0)
		  {
			   pcs_clear_level(l);
			   goto expand_pte_exit;
		  }

		  vpt[PAGE_NUM(TDPAGE)] = naddr | PTE_W | PTE_P;
		  vpt[PAGE_NUM(TSPAGE)] = PTE_ADDR(pte) | PTE_W | PTE_P;
		  tlbflush();

		  memmove(tdpage, tspage, PAGE_SIZE);
		  kpage_dec_ref(PTE_ADDR(pte));

		  pte = (vpt[PAGE_NUM(TDPAGE)] | (pte & PTE_USER) | PTE_W) & ~(uint32_t)PTE_COW;

		  pcs_pop_level(l);
		  tlbflush();

		  err = 0;
     }
     else if (create_perm != NULL)
     {
		  l = pcs_push_level();

		  if ((err = kpage_alloc_push(&naddr)) != 0)
		  {
			   pcs_clear_level(l);
			   goto expand_pte_exit;
		  }

		  pte = naddr | *create_perm | PTE_P;
		  *create_perm = 0;
	  
		  pcs_pop_level(l);
		  tlbflush();

		  err = 0;
     }
     else err = -E_FAULT;

expand_pte_exit:

	 release_pte(idx, pte);
	 return err;
}

int
touch_pte(void *addr, uint32_t *alternative)
{
     uint32_t idx;

     addr = PAGE_ALIGN(addr);
     
     uint32_t l, i;
     int err;
     
     uint32_t cp;
     
     if (alternative == NULL)
     {
		  if ((err = expand_pde(addr, NULL)) != 0)
		  {
			   return err;
		  }
     }
     else
     {
		  cp = *alternative & PTE_USER;
		  if ((err = expand_pde(addr, &cp)) != 0)
		  {
			   return err;
		  }
     }
	  
     idx = PAGE_NUM(addr);

	 uint32_t pte;
     physaddr_t naddr;

	 pte = spinlock_pte(idx);

     if (pte & PTE_P)
     {

		  if (!(pte & PTE_COW))
		  {
			   err = 0;
			   goto touch_pte_exit;
		  }
     
		  l = pcs_push_level();

		  if ((err = kpage_alloc_push(&naddr)) != 0)
		  {
			   pcs_clear_level(l);
			   goto touch_pte_exit;
		  }

		  vpt[PAGE_NUM(TDPAGE)] = naddr | PTE_W | PTE_P;
		  vpt[PAGE_NUM(TSPAGE)] = PTE_ADDR(pte) | PTE_P;
		  tlbflush();

		  memmove(tdpage, tspage, PAGE_SIZE);
		  kpage_dec_ref(PTE_ADDR(pte));
     
		  pte = (vpt[PAGE_NUM(TDPAGE)] | (pte & PTE_USER) | PTE_W) & ~(uint32_t)PTE_COW;

		  pcs_pop_level(l);
		  tlbflush();

		  err = 0;
     }
     else if (alternative != NULL)
     {
		  // cprintf(" [touch_pte] %08x = %08x\n", idx << 12, *alternative | PTE_P);
		  pte = *alternative | PTE_P;
		  *alternative = 0;

		  tlbflush();
	  
		  err = 0;
     }
     else err = -E_FAULT;

touch_pte_exit:

	 release_pte(idx, pte);
	 return err;
}


/* fragment in fork() */


	 /* 突然想不明白为什么要特殊处理了 ... =.= */
	 /* 下次如果明白原因一定要写上来, 嗯 ... */
	 
	 /* special processing for ustack */
	 /* 不需要上锁, 因为每个环境的 stack 是独立的*/
	 
     /* if ((err = kpage_alloc_push(&addr)) != 0) */
     /* { */
	 /* 	  pcs_clear_level(l); */
	 /* 	  tf->regs.eax = err; */
	 /* 	  return; */
     /* } */

     /* tvpd[PAGEDIR_IDX(UTOP - 1)] = addr | */
	 /* 	  (vpd[PAGEDIR_IDX(UTOP - 1)] & PTE_USER); */
     /* vpt[PAGE_NUM(TVPT)] = addr | PTE_W | PTE_P; */
     /* tlbflush(); */

     /* svpt = (ptab_entry_t *)vpt + (PAGEDIR_IDX(UTOP - 1) << (PDX_SHIFT - PTX_SHIFT)); */
     /* for (j = 0; j != PAGETAB_COUNT; ++j) */
     /* { */
	 /* 	  if (svpt[j] & PTE_P) */
	 /* 	  { */
	 /* 		   if ((svpt[j] & (PTE_W | PTE_COW)) && !(svpt[j] & PTE_SHARED)) */
	 /* 		   { */
	 /* 				tvpt[j] = (svpt[j] | PTE_COW) & ~(uint32_t)PTE_W; */
	 /* 				svpt[j] = tvpt[j]; */
	 /* 		   } */
	 /* 		   else tvpt[j] = svpt[j]; */


	 /* 		   kpage_inc_ref(PTE_ADDR(tvpt[j])); */
	 /* 	  } else tvpt[j] = 0; */
     /* } */

     /* if ((err = kpage_alloc_push(&addr)) != 0) */
     /* { */
	 /* 	  pcs_clear_level(l); */
	 /* 	  tf->regs.eax = err; */
	 /* 	  return; */
     /* } */
     /* tvpt[PAGETAB_IDX(UXSTACK_TOP - 1)] = addr | PTE_U | PTE_W | PTE_P; */
     /* vpt[PAGE_NUM(TPAGE)] = addr | PTE_W | PTE_P; */
     /* tlbflush(); */
     
     /* memmove(tpage, (void*)(UXSTACK_TOP - UXSTACK_SIZE), PAGE_SIZE); */

/* 直接拷贝所有使用内存的 fork */
/* void */
/* dumb_fork(struct trapframe_t *tf) */
/* { */
/*      uint32_t l = pcs_push_level(); */
/*      physaddr_t addr; */
/*      ptab_entry_t *svpt; */
/*      int err; */

/*      tf->regs.eax = 0; */

/*      if ((err = kpage_alloc_push(&addr)) != 0) */
/*      { */
/* 		  pcs_clear_level(l); */
/* 		  tf->regs.eax = err; return; */
/*      } */

/*      vpt[PAGE_NUM(TVPD)] = addr | PTE_W | PTE_P; */
/*      tlbflush(); */

/*      memmove(tvpd, (pdir_entry_t *)vpd, PAGE_SIZE); */

/*      tvpd[PAGEDIR_IDX(KVPT)] = addr | PTE_W | PTE_P; */
/*      tvpd[PAGEDIR_IDX(UVPT)] = addr | PTE_U | PTE_P; */
     
/*      /\* copy page dir (user) *\/ */
/*      uint32_t i, j; */
/*      for (i = 0; i != PAGEDIR_IDX(UTOP); ++i) */
/*      { */
/* 		  if (vpd[i] & PTE_P) */
/* 		  { */
/* 			   if ((err = kpage_alloc_push(&addr)) != 0) */
/* 			   { */
/* 					pcs_clear_level(l); */
/* 					tf->regs.eax = err; return; */
/* 			   } */
	       
/* 			   tvpd[i] = addr; */
/* 			   vpt[PAGE_NUM(TVPT)] = addr | PTE_W | PTE_P; */
	       
/* 			   svpt = (ptab_entry_t *)vpt + (i << (PDX_SHIFT - PTX_SHIFT)); */
	       
/* 			   for (j = 0; j != PAGETAB_COUNT; ++j) */
/* 			   { */
/* 					if (!(svpt[j] & PTE_P)) continue; */

/* 					/\* 复制非共享页面 *\/ */
/* 					if (svpt[j] & PTE_SHARED) */
/* 					{ */
/* 						 tvpt[j] = svpt[j]; */
/* 						 kpage_inc_ref(PTE_ADDR(svpt[j])); */
/* 					} */
/* 					else */
/* 					{ */
			 
/* 						 if ((err = kpage_alloc_push(&addr)) != 0) */
/* 						 { */
/* 							  pcs_clear_level(l); */
/* 							  tf->regs.eax = err; return; */
/* 						 } */

/* 						 vpt[PAGE_NUM(TDPAGE)] = addr | PTE_W | PTE_P; */
/* 						 tlbflush(); */
			 
/* 						 tvpt[j] = addr; */
			 
/* 						 memmove(tdpage, PAGE_ADDR(i, j, 0), PAGE_SIZE); */
/* 						 tvpt[j] |= (svpt[j] & PTE_USER); */
/* 					} */
/* 			   } */

/* 			   tvpd[i] = tvpd[i] | (vpd[i] & PTE_USER); */
/* 		  } */
/*      } */

/*      /\* copy page dir kernel *\/ */
/*      if ((err = fork_kern()) != 0) */
/*      { */
/* 		  pcs_clear_level(l); */
/* 		  tf->regs.eax = err; return; */
/*      } */
     
/*      dup_env(tf); */
     
/*      pcs_pop_level(l); */
/*      return; */
/* } */
